<?php

namespace Xrfg\Api\Trading\Spot\Controllers;

use App\Http\Controllers\Controller;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Throwable;
use Xrfg\Api\Trading\Spot\Requests\SymbolRequest;
use Xrfg\Api\Trading\Spot\Resources\TickerResource;
use Xrfg\Core\Trading\Account\Models\Account;
use Xrfg\Api\Trading\Spot\Events\OrderCreatedEvent;
use Xrfg\Api\Trading\Spot\Jobs\CancelOrderJob;
use Xrfg\Core\Trading\Spot\Models\Favorite;
use Xrfg\Core\Trading\Spot\Models\Order;
use Xrfg\Core\Trading\Spot\Models\Pair;
use Xrfg\Api\Trading\Spot\Requests\OrderIndexRequest;
use Xrfg\Api\Trading\Spot\Requests\OrderRequest;
use Xrfg\Api\Trading\Spot\Resources\OrderIndexResource;
use Xrfg\Api\Trading\Spot\Resources\OrderShowResource;
use Xrfg\Core\User\Models\User;

class OrderController extends Controller
{
    /**
     * 订单列表.
     *
     * @param OrderIndexRequest $request
     *
     * @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
     */
    public function index(OrderIndexRequest $request)
    {
        $symbol = $request->query('symbol');
        $coinName = $request->query('coin_name');
        $marketName = $request->query('market_name');
        $side = $request->query('side');
        $status = $request->query('status');
        $orderType = $request->query('order_type');
        $startTime = $request->query('start_time');
        $endTime = $request->query('end_time');
        $time = $request->query('time'); // 1-最近7天 2-7天到3个月
        $perPage = $request->query('per_page', 15);
        $perPage = $perPage > 100 ? 100 : $perPage;

        $orders = Order::where('user_id', Auth::id())
            ->when($symbol, function (Builder $query) use ($symbol) {
                return $query->where('symbol', $symbol);
            })
            ->when(!$symbol && ($coinName || $marketName), function (Builder $query) use ($coinName, $marketName) {
                return $query->where('symbol', 'like', "%{$coinName}/{$marketName}%");
            })
            ->when($side, function (Builder $query) use ($side) {
                return $query->where('side', $side);
            })
            ->when($orderType, function (Builder $query) use ($orderType) {
                return $query->where('type', $orderType);
            })
            ->when($startTime, function (Builder $query) use ($startTime) {
                return $query->where('created_at', '>=', $startTime);
            })
            ->when($endTime, function (Builder $query) use ($endTime) {
                return $query->where('created_at', '<=', $endTime);
            })
            ->when($time == 1, function (Builder $query) { // 1-最近7天
                return $query->where('created_at', '>=', now()->subDays(7));
            })
            ->when($time == 2, function (Builder $query) { // 2-7天至3个月
                return $query->where('created_at', '>=', now()->subMonths(3))->where('created_at', '<', now()->subDays(7));
            })
            ->when(null !== $status, function (Builder $query) use ($status) {
                if (is_numeric($status)) {
                    return $query->where('status', $status);
                } else {
                    return $query->whereIn('status', explode(',', $status));
                }
            })
            ->orderByDesc('id')
            ->paginate($perPage);

        return OrderIndexResource::collection($orders);
    }

    /**
     * 订单详情.
     *
     * @param Order $order
     *
     * @return OrderShowResource
     */
    public function show(Order $order)
    {
        if ($order->user_id !== Auth::id()) {
            $this->error(60001);
        }

        return new OrderShowResource($order);
    }

    /**
     * 创建订单.
     *
     * @param OrderRequest $request
     *
     * @return Response
     *
     * @throws Exception
     */
    public function store(OrderRequest $request)
    {
        $user = Auth::user();
//        $this->checkPayPassword($request, $user);

        $symbol = $request->input('symbol');
        $side = $request->input('side');
        $type = $request->input('order_type');
        $price = $request->input('price');
        $number = $request->input('number');

        $pair = $this->getPair($symbol);

        // 检查余额
        if (Order::SIDE_BUY == $side) {
            $outAccount = Account::firstOrCreate([
                'user_id' => $user->id,
                'coin_id' => $pair->market_id,
            ]);
            Account::firstOrCreate([
                'user_id' => $user->id,
                'coin_id' => $pair->coin_id,
            ]);

            $total = Order::TYPE_LIMIT == $type ? bcmul($price, $number) : $number;

            if (!isset($outAccount->available) || $outAccount->available < $total) {
                $this->error(60007);
            }
            $fee = $pair->buy_fee;
        } else {
            $outAccount = Account::firstOrCreate([
                'user_id' => $user->id,
                'coin_id' => $pair->coin_id,
            ]);
            Account::firstOrCreate([
                'user_id' => $user->id,
                'coin_id' => $pair->market_id,
            ]);
            $total = $number;
            if (!isset($outAccount->available) || $outAccount->available < $total) {
                $this->error(60007);
            }
            $fee = $pair->sell_fee;
        }

        // 生成凭证
        $order = new Order();
        $order->symbol = $pair->symbol;
        $order->user_id = $user->id;
        $order->coin_id = $pair->coin_id;
        $order->market_id = $pair->market_id;
        $order->side = $side;
        $order->type = $type;
        if (Order::TYPE_LIMIT === $type) {
            $order->price = $price;
        }
        $order->number = $number;
        $order->fee = $fee;

        try {
            DB::transaction(function () use ($order, $total, $outAccount) {
                $order->save();
                // 冻结余额
                /** @var Account $outAccount */
                $outAccount = Account::lockForUpdate()->find($outAccount->id);
                // 生成资金流水记录
                $outAccount->availableDecrement($total);
                $outAccount->disabledIncrement($total);
                $outAccount->proofId = [$order->id, Order::CLASS_ID, Order::SIDE_BUY == $order->side ? Order::OP_BUY_NEW : Order::OP_SELL_NEW];
                $outAccount->save();
            });
        } catch (Throwable $exception) {
            Log::error($exception);
            $this->error();
        }

        event(new OrderCreatedEvent($order));

        return $this->null();
    }

    /**
     * 撤销单个订单.
     *
     * @param Order $order
     *
     * @return Response
     */
    public function cancel(Order $order)
    {
        if ($order->user_id !== Auth::id()) {
            $this->error(60001);
        }

        if (Order::TYPE_MARKET == $order->type) {
            $this->error(60008);
        }

        CancelOrderJob::dispatch($order->id)->onQueue('cancel');

        return $this->null();
    }

    /**
     * 撤销所有订单.
     *
     * @return mixed
     */
    public function cancelAll()
    {
        Order::where('user_id', Auth::id())
            ->where('status', Order::STATUS_NEW)
            ->get()
            ->each(function (Order $order) {
                CancelOrderJob::dispatch($order->id)->onQueue('cancel');
            });

        return $this->null();
    }

    /**
     * @param OrderRequest $request
     * @param $user
     */
    private function checkPayPassword(OrderRequest $request, User $user)
    {
        if (!$user->pay_password) {
            $this->error(60002);
        }

        switch ($user->pay_password_type) {
            case 0: // 每次都验证
                if (!$request->has('pay_password')) {
                    $this->error(60003);
                }
                if (!Hash::check($request->input('pay_password'), $user->pay_password)) {
                    $this->error(60004);
                }
                break;
            case 1: // 不验证
                break;
            case 2: // 每2小时验证
                if (!Cache::has('pay_password:'.$user->id)) {
                    if (!$request->has('pay_password')) {
                        $this->error(60003);
                    }
                    if (!Hash::check($request->input('pay_password'), $user->pay_password)) {
                        $this->error(60004);
                    }

                    Cache::put('pay_password:'.$user->id, 1, now()->addHours(2));
                }
                break;
        }
    }

    /**
     * @param $symbol
     *
     * @return Pair
     */
    private function getPair(string $symbol)
    {
        $pair = Pair::where('symbol', $symbol)->first();

        if (is_null($pair)) {
            $this->error(60005);
        }

        if (1 !== $pair->status) {
            $this->error(60006);
        }

        return $pair;
    }

    /**
     * 自选.
     */
    public function symbol(SymbolRequest $request)
    {
        /** @var User $user */
        $user = auth()->user();
        $symbol = $request->input('symbol');
        $op = $request->input('op');

        if ($op == 1) { // 收藏
            Favorite::firstOrCreate([
                'user_id' => $user->id,
                'symbol' => $symbol
            ]);
        } else { // 取消收藏
            Favorite::where('user_id', $user->id)->where('symbol', $symbol)->delete();
        }

        return $this->array();
    }

    /**
     * 自选列表.
     */
    public function mySymbol()
    {
        $pairs = Pair::whereIn('symbol', Favorite::where('user_id', auth()->user()->id)->pluck('symbol'))->get();

        return TickerResource::collection($pairs);
    }
}
